package com.ouwen.smartpay.service.chain.slots.datagram;

import cn.hutool.core.bean.BeanUtil;
import com.fm.common.emuns.ResponseCode;
import com.fm.core.common.boot.Apps;
import com.fm.core.common.enums.TranStatus;
import com.fm.core.common.facade.BizResultBase;
import com.fm.core.common.facade.MerchOrderBase;
import com.fm.core.common.facade.MerchResultBase;
import com.fm.core.utils.Strings;
import com.google.common.collect.Maps;
import com.ouwen.smartpay.domain.ProviderWarpper;
import com.ouwen.smartpay.domain.ServiceWarpper;
import com.ouwen.smartpay.entity.OrderDetail;
import com.ouwen.smartpay.entity.OrderInfo;
import com.ouwen.smartpay.exception.DatagramSlotException;
import com.ouwen.smartpay.exception.OpenApiException;
import com.ouwen.smartpay.openapi.order.ServiceOrder;
import com.ouwen.smartpay.openapi.result.ServiceResult;
import com.ouwen.smartpay.service.OrderDetailService;
import com.ouwen.smartpay.service.chain.context.Context;
import com.ouwen.smartpay.service.chain.slotchain.AbstractLinkedProcessorSlot;
import com.ouwen.smartpay.service.chain.slots.SoltArgsToObjects;
import com.ouwen.smartpay.service.dubbo.RegistryContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;

/**
 * @author <a href="http://youngitman.tech">青年IT男</a>
 * @version v1.0.0
 * @className DatagramSlot
 * @description 请求报文转换
 * @JunitTest: {@link  }
 * @date 2020-04-23 11:39
 **/
public class DatagramSlot extends AbstractLinkedProcessorSlot<ServiceOrder> {

    Logger logger = LoggerFactory.getLogger(DatagramSlot.class);

    @Override
    public void entry(Context context, ServiceOrder order, Object... args) throws Throwable {

        try {

            //请求体转换为业务Order
            MerchOrderBase busOrder = convertToBusinessOrder(order);

            //业务提供者需要的Order
            context.setOrder(busOrder);

            //原始请求报文体
            context.setDatas(order.getDatas());

            fireEntry(context, order, args);

        } catch (Exception e) {

            logger.error("数据报文转换异常:", e);

            BizResultBase result = new BizResultBase();

            result.setRespCode(ResponseCode.FAIL.code());

            result.setRespMsg(ResponseCode.FAIL.message());

            result.setDescription(e.getMessage());

            exit(context, order, args, result);

        }

    }

    /**
     * @param context current {@link Context}
     * @param args    正常调用服务返回结果：
     *                [0]: ServiceOrder, [1]:[ [0]ServiceResult],[2]:BizResult
     *                或者幂等性校验未通过结果为
     *                [0]: ServiceOrder, [1]:[ [0]ServiceResult],[2]:OrderInfo
     */
    @Override
    public void exit(Context context, Object... args) {

        ServiceResult serviceResult = SoltArgsToObjects.toServiceResult(args);

        //结果消息体
        Map<String, Object> datas = Maps.newHashMap();

        //幂等性校验阻断
        if (context.isIdempotentBreak()) {

            idempotentBreak(context, serviceResult, args);

        } else {//正常返回

            MerchResultBase bizResult = SoltArgsToObjects.toMerchResult(args);

            //设置公共参数
            serviceResult.setRequestNo(context.getRequestNo());
            serviceResult.setService(context.getService());
            serviceResult.setGid(context.getGid());
            serviceResult.setPartnerId(context.getPartnerId());
            serviceResult.setSubPartnerId(context.getSubPartnerId());

            //设置附加参数
            serviceResult.setParameters(bizResult.getParameters());
            serviceResult.setDescription(bizResult.getDescription());

            try {

                bizResult.setGid(context.getGid());

                bizResult.setMerchOrderNo(context.getOrder().getMerchOrderNo());

                //datas = BeanUtil.transBean2Map(bizResult);
                datas = BeanUtil.beanToMap(bizResult);

            } catch (Exception e) {

                logger.error("OpenApi结果转换异常:", e);
            }

            serviceResult.setDatas(datas);

        }

        fireExit(context, args);
    }

    /***
     *
     * 幂等返回结果处理
     *
     * @author liyong
     * @date 11:05 AM 2020/6/2
     * @param context
     * @param serviceResult
     * @param args
     * @exception
     * @return ServiceResult
     **/
    private ServiceResult idempotentBreak(Context context, ServiceResult serviceResult, Object... args) {

        //结果消息体
        Map<String, Object> datas = Maps.newHashMap();

        ServiceOrder serviceOrders = (ServiceOrder) args[0];

        //设置公共参数
        serviceResult.setRequestNo(serviceOrders.getRequestNo());

        serviceResult.setService(serviceOrders.getService());

        serviceResult.setGid(serviceOrders.getGid());

        serviceResult.setPartnerId(serviceOrders.getPartnerId());

        serviceResult.setSubPartnerId(serviceOrders.getSubPartnerId());

        Object object = args[2];

        //1.幂等防重，直接返回时这里数据为空
        if (object == null) {

            MerchResultBase bizResult = new MerchResultBase();

            try {

                bizResult.setTranDesc(TranStatus.FTS03.getMessage());

                bizResult.setTranStatus(TranStatus.FTS03);

                bizResult.setRespCode(ResponseCode.FAIL.code());

                bizResult.setRespMsg(ResponseCode.FAIL.message());

                bizResult.setGid(context.getGid());

                bizResult.setDescription("requestNo请求流水号重复");

                bizResult.setMerchOrderNo(context.getOrder().getMerchOrderNo());

                //datas = BeanUtil.transBean2Map(bizResult);
                datas = BeanUtil.beanToMap(bizResult);

            } catch (Exception e) {

                logger.error("OpenApi结果转换异常:", e);
            }

            serviceResult.setDatas(datas);

        } else {//2.幂等处理时获取终态数据状态返回

            if (object instanceof OrderInfo) {

                OrderInfo orderInfo = (OrderInfo) object;

                //获取响应体
                OrderDetailService orderDetailService = Apps.getApplicationContext().getBean(OrderDetailService.class);

                OrderDetail orderDetail = orderDetailService.findOrderDetailResponseInfoByOrderId(orderInfo.getId(), orderInfo.getPartnerId(), orderInfo.getSubPartnerId());

                if (orderDetail != null) {

                    //存在结果直接返回
                    serviceResult.setDatas(BeanUtil.beanToMap(orderDetail.getResponseInfo()));

                }

            } else {

                logger.error("幂等性校验返回的Order类型必须为OrderInfo");

                throw new DatagramSlotException("幂等性校验异常");
            }

        }

        return serviceResult;

    }

    /***
     *
     * 转换请求的业务订单为服务接口定义order
     *
     * @author liyong
     * @date 13:56 2020-04-22
     * @param order
     * @exception
     * @return OrderBase
     **/
    private MerchOrderBase convertToBusinessOrder(ServiceOrder order) {

        RegistryContainer registryContainer = Apps.getApplicationContext().getBean(RegistryContainer.class);

        //查找服务提供者信息，通过service+version+group约束查找
        ProviderWarpper providerWarpper = registryContainer.findServiceInterfaceClassPath(
                ServiceWarpper.builder().serviceName(order.getService()).version(order.getVersion())
                        .group(Strings.isEmpty(order.getGroup()) ? "" : order.getGroup()).build());

        //后端未暴露相关服务
        if (registryContainer.getDefalutProviderWarpper() == providerWarpper) {
            throw new OpenApiException(String.format("未查询到服务【{%s}】", order.getService()));
        }

        //获取服务提供方接口参数Order的Class类型
        Class<?> orderClass = providerWarpper.getOrder();

        //服务提供方Order必须实现MerchOrderBase基类
        if (orderClass != null && MerchOrderBase.class.isAssignableFrom(orderClass)) {

            try {

                Map<String, Object> datas = order.getDatas();

                //实例对象 Map -> order
                //BeanUtil.transMap2Bean(datas, orderBase);
                MerchOrderBase orderBase = (MerchOrderBase) BeanUtil.mapToBean(datas, orderClass, true);

                //子order传递gid
                orderBase.setGid(order.getGid());

                orderBase.setPartnerId(order.getPartnerId());

                orderBase.setSubPartnerId(order.getSubPartnerId());

                /*交易类请求直接使用RequestNo,请求数据体中不传递MerchOrderNo == RequestNo，
                 *查询类请求RequestNo每次必须不同且请求数据体中传递MerchOrderNo != RequestNo
                 */
                if (!isQueryService(order.getService())) {

                    orderBase.setMerchOrderNo(order.getRequestNo());

                }

                //请求体传递gid 、paretnid、subPartnerId无效
                datas.put("gid", order.getGid());

                datas.put("partnerId", order.getPartnerId());

                datas.put("subPartnerId", order.getSubPartnerId());

                return orderBase;

            } catch (Exception e) {

                logger.error("转换业务Order异常:", e);

                throw new DatagramSlotException("转换业务Order异常");

            }
        } else {

            throw new DatagramSlotException("服务提供放Facade接口必须实现OrderBase基类");
        }

    }

    /***
     *
     * 判断服务服务是否是请求服务 注意：这里需要约定请求类服务必须以query开头
     *
     * @author liyong
     * @date 9:57 AM 2020/6/2
     * @param service
     * @exception
     * @return boolean
     **/
    private boolean isQueryService(String service) {

        if (Strings.isEmpty(service) || !service.startsWith("query")) {

            return false;

        }

        return true;
    }

}
